package server;




import java.io.EOFException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Calendar;

import lp.polimi.treefinder.utils.ManagingMessage;
import lp.polimi.treefinder.utils.ManagingMessage.Type;
import lp.polimi.treefinder.utils.RankElement;

import org.opencv.core.Mat;
import org.opencv.highgui.Highgui;

import utils.ImageUtils;
import utils.Species;
import featuresProcessing.KNNBasedRankGenerator;
import featuresProcessing.MatchingSystem;

/** 
 * TCP implementation of Connection. The server runs each connection to a client on a different thread.
 */
public class TCPConnection extends Thread implements Connection {

	protected GenericSocket socket;
	protected ObjectOutputStream oos;
	protected ObjectInputStream ois;

	private ManagingMessage managingMessage;

	public TCPConnection (GenericSocket socket) {
		this.socket = socket;
	}

	@Override
	public void startListening() {
		start();
	}

	@Override
	public void run() {
		try {

			while (true) {

				managingMessage = receive();
				
				if (managingMessage.getType()==Type.MATCHING_REQUEST) {
					Species newSpecies = new Species();

					byte[] image = (byte[])managingMessage.getProperty("image");
					String fileName = Long.toString(Calendar.getInstance().getTimeInMillis())+".png";
					FileOutputStream fos = new FileOutputStream(new File(fileName));
					fos.write(image);
					fos.close();
					Mat imageMat = Highgui.imread(fileName, Highgui.IMREAD_GRAYSCALE);
					new File(fileName).delete();
					Mat features = MatchingSystem.getSharedInstance().getFeaturesDetector().getDescriptors(imageMat);
					Mat shapeDescriptors = MatchingSystem.getSharedInstance().getShapeDetector().getDescriptors(imageMat);
					newSpecies.setFeatures(features);
					newSpecies.setShapeDescriptors(shapeDescriptors);
					

					ArrayList<RankElement> rank = MatchingSystem.getSharedInstance().classify(newSpecies, new KNNBasedRankGenerator());

					ManagingMessage ranking = new ManagingMessage(ManagingMessage.Type.RANKING);
					ranking.putProperty("ranking", rank);
					
					try {
						send(ranking);
						Server.getSharedInstance().getFeedbackManager().printMessage("Sent ranking to "+socket.getIPAddress(), false);
					} catch (IOException e1) {
						Server.getSharedInstance().getFeedbackManager().printMessage("ERROR: cant' send the ranking to "+socket.getIPAddress(), false);
						Server.getSharedInstance().getUsersManager().removeUser(socket.getIPAddress().toString());
						e1.printStackTrace();
					} 
				}
			}
		} catch (EOFException e) {
			Server.getSharedInstance().getFeedbackManager().printMessage("Closed connection to "+socket.getIPAddress(), false);
		} catch (Exception e2) {
			e2.printStackTrace();
		} finally {
			closeSocket();
		}

	}

	public void send(ManagingMessage managingMessage) throws IOException {
		if (oos == null)
			oos = new ObjectOutputStream(socket.getOutputStream());

		oos.writeObject(managingMessage);	

	}

	public ManagingMessage receive() throws Exception {
		if (ois == null)
			ois = new ObjectInputStream(socket.getInputStream());

		ManagingMessage message = (ManagingMessage)ois.readObject();

		return message;
	}

	public void closeSocket() {

		try {
			if (oos != null)
				oos.close();
			if (ois != null)
				ois.close();
		} catch (IOException e) {
			// do nothing
		}
		socket.close();

	}

	@Override
	public GenericSocket getSocket() {
		return socket;
	}



}
